#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>

static const int defaultSize = 5;

template<class T>
class BlockQueue
{
    bool Empty() 
    {
        return _queue.empty();
    }
    bool Full()
    {
        return _queue.size() == _capacity;
    }
public:
    BlockQueue(int capacity = defaultSize)
        :_capacity(defaultSize), _wait_consumer(0), _wait_productor(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_consumer_cond, nullptr);
        pthread_cond_init(&_productor_cond, nullptr);
    }

    //生产者接口
    void EnQueue(const T& t)
    {
        pthread_mutex_lock(&_mutex);

        while(Full())
        {
            _wait_productor++;
            pthread_cond_wait(&_productor_cond, &_mutex);
            _wait_productor--;
        }
        _queue.emplace(t);

        if(_wait_consumer > 0)
            pthread_cond_signal(&_consumer_cond);

        pthread_mutex_unlock(&_mutex);
    }

    //消费者专用接口
    void Dequeue(T& t)
    {
        pthread_mutex_lock(&_mutex);
        while(Empty())
        {
            _wait_consumer++;
            pthread_cond_wait(&_consumer_cond, &_mutex);
            _wait_consumer--;
        }
        t = _queue.front();
        _queue.pop();

        if(_wait_productor > 0)
            pthread_cond_signal(&_productor_cond);

        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_consumer_cond);
        pthread_cond_destroy(&_productor_cond);

    }

private:
    std::queue<T> _queue;
    int _capacity;

    pthread_mutex_t _mutex;

    pthread_cond_t _consumer_cond;    //消费者条件变量
    pthread_cond_t _productor_cond;   //生产者条件变量

    int _wait_consumer; //等待消费
    int _wait_productor; //等待生产

};